﻿using System.Diagnostics;
using NLog;
using System.IO.Ports;
using System.Text;
using ToolLibrary;
using Microsoft.VisualBasic;

namespace SerialTools {
    /// <summary>
    /// 
    /// </summary>
    public class SerialPortHelper {
        /// <summary>
        /// 
        /// </summary>
        Logger log = LogManager.GetCurrentClassLogger();

        /// <summary>
        /// 
        /// </summary>
        private readonly SerialPort sp = new();

        /// <summary>
        /// 
        /// </summary>
        private readonly object _lockObject = new();

        /// <summary>
        /// 
        /// </summary>
        public long ReceiveCount { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        public long SendCount { get; private set; }

        public string PortName => sp.PortName;

        public bool IsOpen => sp.IsOpen;

        private DateTime _lastSendTime = DateTime.MinValue;
        private DateTime _lastReceiveTime = DateTime.MinValue;

        // 定义一个事件  
        public event Action<long>? ReportReceiveCountEvent;
        public event Action<long>? ReportSendCountEvent;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="portName"></param>
        /// <param name="baudRate"></param>
        public SerialPortHelper(string portName = "COM1", int baudRate = 9600) {
            // 配置串口参数
            sp.PortName = portName; // 串口号
            sp.BaudRate = baudRate; // 波特率
            sp.Handshake = Handshake.None; // 流控制类型  
            //sp.ReadTimeout = 500; // 读取超时时间  
            //sp.WriteTimeout = 500; // 写入超时时间 

            sp.DataReceived += DataReceivedHandler;
        }

        /// <summary>
        /// 
        /// </summary>
        public void Open() {
            if (sp.IsOpen) return;

            sp.Parity = Program.Config.Parity; // 校验位
            sp.DataBits = Program.Config.DataBits; // 数据位
            sp.StopBits = Program.Config.StopBits; // 停止位
            sp.Open();
        }

        public void Close() {
            sp.Close();
        }


        /// <summary>
        /// 查找端口
        /// </summary>
        public string[] FindPorts() {
            string[] portList = SerialPort.GetPortNames();
            if (portList.Length <= 0) {
                log.Error("没有查找到可用端口");
                return portList;
            }
            // 替换字符串中的 "com"  
            int[] newStrings = portList.Select(s => s.Replace("COM", "").ToInt()).ToArray();
            Array.Sort(newStrings);
            portList = newStrings.Select(p => "COM" + p).ToArray();

            return portList;
        }

        /// <summary>
        /// 发送接收数据
        /// </summary>
        /// <param name="sendBytes"></param>
        /// <returns></returns>
        public byte[] SendRec(byte[] sendBytes) {
            lock (_lockObject) {
                var recBytes = Array.Empty<byte>();
                if (sp.IsOpen == false) {
                    log.Warn($"{sp.PortName} 未打开 ");
                    return recBytes;
                }
                try {
                    log.Debug($"{(Program.Config.LogPort ? (sp.PortName + " ") : "")}发送 {sendBytes.ToArray().ByteToHexString()}");
                    sp.Write(sendBytes, 0, sendBytes.Length);
                    // 暂停n毫秒，等待数据接收，正常10毫秒内就应该有返回
                    Thread.Sleep(Program.Config.DelayMsOnReceiveData);
                    // 等待接收数据
                    var startTime = DateTime.Now;
                    var timeout = TimeSpan.FromSeconds(3);    // 超时时间 n 秒

                    while (true) {
                        if (sp.BytesToRead > 0) {
                            // 从串口读取数据
                            recBytes = new byte[sp.BytesToRead];
                            sp.Read(recBytes, 0, recBytes.Length);

                            log.Debug($"{(Program.Config.LogPort ? (sp.PortName + " ") : "")}接收 {recBytes.ToArray().ByteToHexString()}");
                            return recBytes;
                        }

                        if (DateTime.Now - startTime > timeout) {
                            // 超时，未接收到数据
                            log.Warn($"{sp.PortName} 通讯超时 ");
                            return recBytes;
                        }
                        // 暂停100毫秒，等待数据接收
                        Thread.Sleep(100);
                    }
                } catch (Exception ex) {
                    log.Error(ex);
                }
                return recBytes;
            }
        }

        /// <summary>
        /// 发送接收数据
        /// </summary>
        /// <param name="sendBytes"></param>
        /// <returns></returns>
        public void Send(byte[] sendBytes) {
            //lock (_lockObject) {
            //    if (sp.IsOpen == false) {
            //        log.Error($"{sp.PortName} 未打开 ");
            //        return;
            //    }

            log.Debug($"{(Program.Config.LogPort ? (sp.PortName + " ") : "")}发送 {sendBytes.ByteToHexString()}");
            //    if (Program.Config.StatCommTime) _lastSendTime = DateTime.Now;
            sp.Write(sendBytes, 0, sendBytes.Length);

                //SendCount += sendBytes.Length;
                //ReportSendCountEvent?.Invoke(SendCount);
            //}
        }

        private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) {
            if (Program.Config.DelayMsOnReceiveData > 0)
                Thread.Sleep(Program.Config.DelayMsOnReceiveData);

            double commTime = 0;
            if (_lastSendTime > DateTime.MinValue) {
                _lastReceiveTime = DateTime.Now;
                commTime = Math.Round((_lastReceiveTime - _lastSendTime).TotalMilliseconds, 3);
                _lastSendTime = DateTime.MinValue;
            }

            StringBuilder sbLog = new StringBuilder($"{(Program.Config.LogPort ? (sp.PortName + " ") : "")} ");
            // 从串口接收数据  
            if (Program.Config.ReceiveHex) {
                SerialPort sp = (SerialPort)sender;
                int readLen = sp.BytesToRead;
                byte[] receiveBytes = new byte[readLen];
                sp.Read(receiveBytes, 0, readLen);

                // 处理接收到的数据 
                sbLog.Append($"接收: {receiveBytes.ByteToHexString()} ");

                ReceiveCount += readLen;
            } else {
                SerialPort serialPort = (SerialPort)sender;
                int readLen = sp.BytesToRead;
                byte[] receiveBytes = new byte[readLen];
                sp.Read(receiveBytes, 0, readLen);
                string data = Encoding.UTF8.GetString(receiveBytes);

                // 处理接收到的数据  
                sbLog.Append($"接收: {data} ");
                if (Program.Config.ShowHexOnAscii)
                    sbLog.Append($"HEX: {receiveBytes.ByteToHexString()} ");

                ReceiveCount += data.Length;
            }

            if (commTime > 0)
                sbLog.Append($"耗时：{commTime} 毫秒");
            log.Debug(sbLog);

            ReportReceiveCountEvent?.Invoke(ReceiveCount);
        }

        // class end
    }
}
